package gagak.framework.web.paramresovler.requestparam;


import gagak.framework.web.annonation.Nullable;
import gagak.framework.web.annonation.RequestParam;
import gagak.framework.web.annonation.ValueConstants;
import gagak.framework.web.exception.MissingServletRequestParameterException;
import gagak.framework.web.multipart.MultipartException;
import gagak.framework.web.multipart.MultipartFile;
import gagak.framework.web.multipart.MultipartRequest;
import gagak.framework.web.multipart.support.MissingServletRequestPartException;
import gagak.framework.web.multipart.support.MultipartResolutionDelegate;
import gagak.framework.web.param.MethodParameter;
import gagak.framework.web.support.request.NativeWebRequest;
import gagak.framework.web.utils.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;


public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {

    private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);

    // 这个参数老重要了：
    // true：表示参数类型是基本类型 参考BeanUtils#isSimpleProperty(什么Enum、Number、Date、URL、包装类型、以上类型的数组类型等等)
    // 如果是基本类型，即使你不写@RequestParam注解，它也是会走进来处理的~~~(这个@PathVariable可不会哟~)
    // fasle：除上以外的。  要想它处理就必须标注注解才行哦，比如List等~
    // 默认值是false
    private final boolean useDefaultResolution;


    // 此构造只有`MvcUriComponentsBuilder`调用了  传入的false
    public RequestParamMethodArgumentResolver(boolean useDefaultResolution) {
        this.useDefaultResolution = useDefaultResolution;
    }


    // 此处理器能处理如下Case：
    // 1、所有标注有@RequestParam注解的类型（非Map）/ 注解指定了value值的Map类型（自己提供转换器哦）
    // ======下面都表示没有标注@RequestParam注解了的=======
    // 1、不能标注有@RequestPart注解，否则直接不处理了
    // 2、是上传的request：isMultipartArgument() = true（MultipartFile类型或者对应的集合/数组类型  或者javax.servlet.http.Part对应结合/数组类型）
    // 3、useDefaultResolution=true情况下，"基本类型"也会处理
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        if (parameter.hasParameterAnnotation(RequestParam.class)) {
            if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
                RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
                return (requestParam != null && StringUtils.hasText(requestParam.name()));
            } else {
                return true;
            }
        } else {

            parameter = parameter.nestedIfOptional();

            return false;
        }
    }


    @Override
    protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
        RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);
        return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo());
    }

    @Override
    @Nullable
    protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
        HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);


        // 这块解析出来的是个MultipartFile或者其集合/数组 // TODO: 2022/12/1
       /* if (servletRequest != null) {
            Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
            if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
                return mpArg;
            }
        }*/


        //小知识点：getParameter()其实本质是getParameterNames()[0]的效果
        // 强调一遍：?ids=1,2,3 结果是["1,2,3"]（兼容方式，不建议使用。注意：只能是逗号分隔）
        // ?ids=1&ids=2&ids=3  结果是[1,2,3]（标准的传值方式，建议使用）
        // 但是Spring MVC这两种都能用List接收  请务必注意他们的区别~~~
        Object arg = null;
        MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
        if (multipartRequest != null) {
            List<MultipartFile> files = multipartRequest.getFiles(name);
            if (!files.isEmpty()) {
                arg = (files.size() == 1 ? files.get(0) : files);
            }
        }
        if (arg == null) {
            String[] paramValues = request.getParameterValues(name);
            if (paramValues != null) {
                arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
            }
        }
        return arg;
    }

    @Override
    protected void handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request)
            throws Exception {

        HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
        if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
            if (servletRequest == null || !MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
                throw new MultipartException("Current request is not a multipart request");
            } else {
                throw new MissingServletRequestPartException(name);
            }
        } else {
            throw new MissingServletRequestParameterException(name,
                    parameter.getNestedParameterType().getSimpleName());
        }
    }


    private static class RequestParamNamedValueInfo extends NamedValueInfo {

        public RequestParamNamedValueInfo() {
            super("", false, ValueConstants.DEFAULT_NONE);
        }

        public RequestParamNamedValueInfo(RequestParam annotation) {
            super(annotation.name(), annotation.required(), annotation.defaultValue());
        }
    }

}
